<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>控制器</title>
    <style>
    *{box-sizing:border-box;margin:0;padding:0}
    body{display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:100vh;background-color:#fff;font-family:'Segoe UI',Arial,sans-serif;padding:20px}
    .page-title{color:#333;margin-bottom:20px;text-align:center;font-weight:600;font-size:1.5rem}
    
    /* 摇杆容器 - 关键修改：使用flex-wrap并设置固定间距 */
    .joysticks-container{
        display:flex;
        gap:15px; /* 摇杆之间的间距 */
        align-items:center;
        justify-content:center;
        flex-wrap:wrap; /* 允许换行 */
        width:100%;
        max-width:1200px;
        padding:10px 0;
    }
    
    /* 滚动条美化 */
    .joysticks-container::-webkit-scrollbar{height:6px;}
    .joysticks-container::-webkit-scrollbar-thumb{background-color:#4a90e2;border-radius:3px;}
    .joysticks-container::-webkit-scrollbar-track{background-color:#f1f1f1;border-radius:3px;}
    
    /* 摇杆项 - 关键修改：固定为50%宽度（减去间距） */
    .joystick-item{
        display:flex;
        flex-direction:column;
        align-items:center;
        gap:10px;
        width:calc(50% - 10px); /* 50%宽度减去一半间距 */
        min-width:unset; /* 移除最小宽度限制 */
        padding:0 5px;
    }
    
    .joystick-label{
        color:#333;
        font-weight:500;
        font-size:14px;
        text-align:center;
        padding:3px 8px;
        border-radius:4px;
        background-color:rgba(255,255,255,.8);
        white-space:nowrap;
    }
    
    .joystick-wrapper{
        display:flex;
        position:relative;
        width:100%; /* 占满父容器宽度 */
        justify-content:center;
    }
    
    .joystick-wrapper.vertical{flex-direction:column;align-items:center;}
    .joystick-wrapper.horizontal{flex-direction:row;align-items:center;}
    
    .joystick-track{
        position:relative;
        border:2px solid #4a90e2;
        border-radius:30px;
        overflow:hidden;
    }
    
    .joystick-track:hover{box-shadow:0 6px 16px rgba(0,0,0,.15);}
    
    /* 轨道尺寸 - 水平轨道占满50%屏幕宽度，垂直轨道固定宽度 */
    .joystick-track.vertical{width:60px;height:300px;}
    .joystick-track.horizontal{width:100%;max-width:300px;height:60px;}
    
    .joystick-handle{
        position:absolute;
        width:50px;
        height:50px;
        background-color:#4a90e2;
        border-radius:50%;
        cursor:grab;
        transition:transform .1s ease-out,background-color .2s ease;
        user-select:none;
        -webkit-user-select:none;
        border:2px solid rgba(255,255,255,.7);
        display:flex;
        align-items:center;
        justify-content:center;
        color:white;
        font-weight:600;
        font-size:14px;
    }
    
    .joystick-handle:active{
        cursor:grabbing;
        background-color:#357abd;
        transform:scale(.95);
    }
    
    .joystick-wrapper.vertical .joystick-handle{left:5px;top:125px;}
    .joystick-wrapper.horizontal .joystick-handle{left:calc(50% - 25px);top:5px;} /* 水平居中 */
    
    .usage-info{
        margin-top:30px;
        color:#666;
        font-size:14px;
        text-align:center;
        max-width:600px;
        line-height:1.5;
    }
    
    /* 平板设备适配 */
    @media (max-width:1024px){
        .joystick-track.vertical{width:50px;height:250px;}
        .joystick-track.horizontal{height:50px;}
        .joystick-handle{width:40px;height:40px;font-size:12px;}
        .joystick-wrapper.vertical .joystick-handle{left:5px;top:105px;}
        .joystick-wrapper.horizontal .joystick-handle{left:calc(50% - 20px);top:5px;}
    }
    
    /* 手机设备适配 - 确保2个一行 */
    @media (max-width:768px){
        body{padding:10px 5px;}
        .page-title{margin-bottom:15px;font-size:1.2rem;}
        .joysticks-container{gap:10px;}
        .joystick-item{width:calc(50% - 5px);} /* 调整间距计算 */
        .joystick-track.vertical{width:45px;height:200px;}
        .joystick-handle{width:35px;height:35px;font-size:12px;}
        .joystick-wrapper.vertical .joystick-handle{left:5px;top:82.5px;}
        .joystick-wrapper.horizontal .joystick-handle{left:calc(50% - 17.5px);top:5px;}
    }
    
    /* 小屏手机额外适配 */
    @media (max-width:375px){
        .joystick-track.vertical{width:40px;height:180px;}
        .joystick-handle{width:30px;height:30px;font-size:11px;}
        .joystick-wrapper.vertical .joystick-handle{left:5px;top:75px;}
        .joystick-wrapper.horizontal .joystick-handle{left:calc(50% - 15px);top:5px;}
        .joystick-label{font-size:12px;}
    }
    
    .button-container {
        display: flex;
        gap: 50px; 
        margin-bottom: 20px;
        width: 100%;
        justify-content: center;
    }
    
    .control-btn {
        padding: 12px 24px;
        background: #fff;
        color: #2196F3;
        border: 2px solid #2196F3;
        border-radius: 6px;
        cursor: pointer;
        transition: all 0.3s;
    }
    
    .control-btn:active {
        background-color: #2196F3;
        color: white;
    }
    
    .connection-status {
        margin-bottom: 15px;
        padding: 6px 12px;
        border-radius: 4px;
        font-weight: 500;
        font-size: 14px;
    }
    
    .status-connected {
        background-color: #e8f5e9;
        color: #2e7d32;
    }
    
    .status-disconnected {
        background-color: #ffebee;
        color: #c62828;
    }
    </style>
</head>
<body onload="socket_init()">
    <h1 class="page-title">控制器</h1>
    
    <div class="connection-status status-disconnected" id="connectionStatus">未连接</div>
    
    <div class="button-container">
        <button class="control-btn" id="blink">blink</button>
        <button class="control-btn" id="dir">jump</button>
    </div>
    
    <div class="joysticks-container">
        <!-- 第一行第一个：水平摇杆 -->
        <div class="joystick-item">
            <div class="joystick-label">摇摆</div>
            <div id="roll" class="joystick-wrapper horizontal"></div>
        </div>
        
        <!-- 第一行第二个：水平摇杆 -->
        <div class="joystick-item">
            <div class="joystick-label">左右</div>
            <div id="joy_x" class="joystick-wrapper horizontal"></div>
        </div>
        
        <!-- 第二行第一个：垂直摇杆 -->
        <div class="joystick-item">
            <div class="joystick-label">高度</div>
            <div id="height" class="joystick-wrapper vertical"></div>
        </div>
        
        <!-- 第二行第二个：垂直摇杆 -->
        <div class="joystick-item">
            <div class="joystick-label">前后</div>
            <div id="joy_y" class="joystick-wrapper vertical"></div>
        </div>
    </div>
    
    <script>
        var socket; // WebSocket实例
        var isConnected = false; // 连接状态
        
        // 更新连接状态显示
        function updateConnectionStatus(connected) {
            isConnected = connected;
            const statusElement = document.getElementById('connectionStatus');
            if (connected) {
                statusElement.textContent = '已连接';
                statusElement.className = 'connection-status status-connected';
            } else {
                statusElement.textContent = '未连接';
                statusElement.className = 'connection-status status-disconnected';
            }
        }
        
        // 初始化WebSocket连接
        function socket_init() {
            try {
                // 根据环境选择合适的连接方式
                const wsProtocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
                socket = new WebSocket(wsProtocol + '//' + window.location.hostname + ':81/');
                
                // 连接成功事件
                socket.onopen = function() {
                    console.log('WebSocket连接已建立');
                    updateConnectionStatus(true);
                };
                
                // 接收消息事件
                socket.onmessage = function(event) {
                    console.log('收到消息:', event.data);
                    // 可以在这里处理从服务器返回的消息
                };
                
                // 连接关闭事件
                socket.onclose = function() {
                    console.log('WebSocket连接已关闭');
                    updateConnectionStatus(false);
                    // 尝试重连
                    setTimeout(socket_init, 3000);
                };
                
                // 错误事件
                socket.onerror = function(error) {
                    console.error('WebSocket错误:', error);
                    updateConnectionStatus(false);
                };
            } catch (e) {
                console.error('WebSocket初始化失败:', e);
                updateConnectionStatus(false);
                // 重试连接
                setTimeout(socket_init, 3000);
            }
        }

        // 摇杆控制器类
        class Joystick {
            constructor(containerId, direction = 'vertical', min = -100, max = 100) {
                this.container = document.getElementById(containerId);
                this.direction = direction;
                this.id = containerId;
                this.isDragging = false;
                this.startPos = 0;
                this.currentPos = 0;
                this.centerPos = 0;
                this.min = min;
                this.max = max;
                this.range = max - min;
                
                this.createElements();
                this.handle = this.container.querySelector('.joystick-handle');
                this.track = this.container.querySelector('.joystick-track');
                this.initDimensions();
                this.bindEvents();
                this.setHandlePosition(this.centerPos);
            }
            
            createElements() {
                const track = document.createElement('div');
                track.className = `joystick-track ${this.direction}`;
                
                const handle = document.createElement('div');
                handle.className = 'joystick-handle';
                handle.textContent = '0';
                
                track.appendChild(handle);
                this.container.appendChild(track);
            }
            
            initDimensions() {
                if (this.direction === 'vertical') {
                    this.trackSize = this.track.offsetHeight;
                    this.handleSize = this.handle.offsetHeight;
                    this.centerPos = (this.trackSize - this.handleSize) / 2;
                } else {
                    this.trackSize = this.track.offsetWidth;
                    this.handleSize = this.handle.offsetWidth;
                    this.centerPos = (this.trackSize - this.handleSize) / 2;
                }
            }
            
            clamp(value, min, max) {
                return Math.min(Math.max(value, min), max);
            }
            
            updateValue(position) {
                const offset = this.direction === 'vertical' 
                    ? this.centerPos - position 
                    : position - this.centerPos;
                
                const rawPercentage = (offset / this.centerPos) * 100;
                const rawValue = this.clamp(Math.round(rawPercentage), -100, 100);

                const mappedValue = Math.round(
                    ((rawValue + 100) / 200) * this.range + this.min
                );

                this.handle.textContent = mappedValue;
                this.dispatchValueChange({raw: rawValue, mapped: mappedValue});
            }
            
            dispatchValueChange({raw, mapped}) {
                const event = new CustomEvent('valuechange', {
                    detail: {
                        rawValue: raw,
                        mappedValue: mapped,
                        id: this.id,
                        joystick: this
                    },
                    bubbles: true
                });
                this.container.dispatchEvent(event);
            }
            
            setHandlePosition(pos) {
                const minPos = 0;
                const maxPos = this.trackSize - this.handleSize;
                const constrainedPos = this.clamp(pos, minPos, maxPos);
                
                if (this.direction === 'vertical') {
                    this.handle.style.top = `${constrainedPos}px`;
                } else {
                    this.handle.style.left = `${constrainedPos}px`;
                }
                
                this.updateValue(constrainedPos);
            }
            
            returnToCenter() {
                if (!this.isDragging) {
                    const currentPosition = parseFloat(
                        this.direction === 'vertical' 
                            ? this.handle.style.top 
                            : this.handle.style.left
                    ) || this.centerPos;
                    
                    const distance = this.centerPos - currentPosition;
                    
                    if (Math.abs(distance) < 1) {
                        this.setHandlePosition(this.centerPos);
                        return;
                    }
                    
                    const step = distance * 0.5;
                    this.setHandlePosition(currentPosition + step);
                    requestAnimationFrame(() => this.returnToCenter());
                }
            }
            
            bindEvents() {
                this.handle.addEventListener('mousedown', (e) => {
                    this.isDragging = true;
                    this.startPos = this.direction === 'vertical' ? e.clientY : e.clientX;
                    this.currentPos = parseFloat(
                        this.direction === 'vertical' 
                            ? this.handle.style.top 
                            : this.handle.style.left
                    ) || this.centerPos;
                    e.preventDefault();
                });
                
                document.addEventListener('mousemove', (e) => {
                    if (this.isDragging) {
                        const delta = (this.direction === 'vertical' ? e.clientY : e.clientX) - this.startPos;
                        this.setHandlePosition(this.currentPos + delta);
                    }
                });
                
                document.addEventListener('mouseup', () => {
                    if (this.isDragging) {
                        this.isDragging = false;
                        this.returnToCenter();
                    }
                });
                
                this.handle.addEventListener('touchstart', (e) => {
                    this.isDragging = true;
                    this.startPos = this.direction === 'vertical' 
                        ? e.touches[0].clientY 
                        : e.touches[0].clientX;
                    this.currentPos = parseFloat(
                        this.direction === 'vertical' 
                            ? this.handle.style.top 
                            : this.handle.style.left
                    ) || this.centerPos;
                    e.preventDefault();
                });
                
                document.addEventListener('touchmove', (e) => {
                    if (this.isDragging) {
                        const delta = (this.direction === 'vertical' 
                            ? e.touches[0].clientY 
                            : e.touches[0].clientX) - this.startPos;
                        this.setHandlePosition(this.currentPos + delta);
                        e.preventDefault();
                    }
                }, { passive: false });
                
                document.addEventListener('touchend', () => {
                    if (this.isDragging) {
                        this.isDragging = false;
                        this.returnToCenter();
                    }
                });
                
                window.addEventListener('resize', () => {
                    this.initDimensions();
                    if (!this.isDragging) {
                        this.setHandlePosition(this.centerPos);
                    }
                });
            }
        }
        
        // 控制状态对象
        const controlState = {
            blink: 0,
            dir: "stop",
            roll: 0,
            height: 30,
            joy_y: 0,
            joy_x: 0
        };

        // 统一发送函数
        function sendAllData() {
            if (!isConnected) {
                console.log('未连接，无法发送数据');
                return;
            }
            
            const data = {
                dir: controlState.dir,
                roll: controlState.roll,
                height: controlState.height,
                joy_y: controlState.joy_y,
                joy_x: controlState.joy_x,
                blink: controlState.blink,
                stable: 1,
                'mode': 'basic',
            };
            
            try {
                socket.send(JSON.stringify(data));
                console.log('发送数据:', data);
            } catch (e) {
                console.error('发送数据失败:', e);
                updateConnectionStatus(false);
            }
            
            if (controlState.blink === 1) controlState.blink = 0;
            if (controlState.dir === "jump") controlState.dir = "stop";
        }

        // 初始化摇杆实例
        document.addEventListener('DOMContentLoaded', () => {
            const joysticks = [
                new Joystick('roll', 'horizontal', -30, 30),
                new Joystick('height', 'vertical', 0, 60),
                new Joystick('joy_y', 'vertical'),
                new Joystick('joy_x', 'horizontal')
            ];
            
            document.querySelectorAll('.joystick-wrapper').forEach(wrapper => {
                wrapper.addEventListener('valuechange', (e) => {
                    controlState[e.detail.id] = e.detail.mappedValue;
                    sendAllData();
                });
            });
            
            document.querySelectorAll('.control-btn').forEach(btn => {
                btn.addEventListener('mousedown', (e) => {
                    btn.style.backgroundColor = '#2196F3';
                    btn.style.color = 'white';
                    
                    switch(e.target.id) {
                        case 'blink': controlState.blink = 1; break;
                        case 'dir': controlState.dir = "jump"; break;
                    }
                    sendAllData();
                });
                
                btn.addEventListener('mouseup', () => {
                    btn.style.backgroundColor = '';
                    btn.style.color = '';
                });
                
                btn.addEventListener('touchstart', (e) => {
                    btn.style.backgroundColor = '#2196F3';
                    btn.style.color = 'white';
                    
                    switch(e.target.id) {
                        case 'blink': controlState.blink = 1; break;
                        case 'dir': controlState.dir = "jump"; break;
                    }
                    sendAllData();
                    e.preventDefault();
                });
                
                btn.addEventListener('touchend', (e) => {
                    btn.style.backgroundColor = '';
                    btn.style.color = '';
                    e.preventDefault();
                });
            });
        });
    </script>
</body>
</html>